package cn.echogo.framework.core;

import java.lang.ref.WeakReference;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Process;

public class AsyncExecutorImp implements AsyncExecutor {

	private static final int CORE_POOL_SIZE = 5;
	private static final int MAXIMUM_POOL_SIZE = 128;
	private static final int KEEP_ALIVE = 10;

	private static final ThreadFactory sThreadFactory = new ThreadFactory() {
		private final AtomicInteger mCount = new AtomicInteger(1);

		public Thread newThread(Runnable r) {
			return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
		}
	};

	private static final BlockingQueue<Runnable> sWorkQueue = new LinkedBlockingQueue<Runnable>(10);

	private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

	private static final int WHAT_EXECUTE_EXECEPTION = -4444;

	private ConcurrentHashMap<Integer, Future> mFutureMap = new ConcurrentHashMap<Integer, Future>();
	private InternalHandler mInternalHandler;

	public AsyncExecutorImp() {
	}

	@Override
	public void submit(int id, Bundle params) {
		AsyncTaskInternal task = new AsyncTaskInternal(id, mInternalHandler, params);
		Future future = sExecutor.submit(task);
		mFutureMap.put(id, future);
	}

	public void publishResult(int id, Bundle data) {
		Message msg = mInternalHandler.obtainMessage(id);
		msg.setData(data);
		msg.sendToTarget();
	}

	@Override
	public void cancel(int id) {
		Future future = mFutureMap.remove(id);
		if (future != null) {
			future.cancel(true);
		}
	}

	@Override
	public void setAsyncExecuteCallback(AsyncExecuteCallback asyncExecuteCallback) {
		this.mInternalHandler = new InternalHandler(asyncExecuteCallback);
	}

	private static class AsyncTaskInternal implements Runnable {

		private int mId;
		private InternalHandler mInternalHandler;
		private Bundle params;

		public AsyncTaskInternal(int id, InternalHandler internalHandler, Bundle params) {
			this.mId = id;
			this.mInternalHandler = internalHandler;
			this.params = params;
		}
		
		public void run() {
			Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
			AsyncExecuteCallback callback = mInternalHandler.getAsyncExecuteCallback();
			if (callback != null) {
				try {
					callback.onExecute(mId, params);
				} catch (Exception e) {
					Message msg = mInternalHandler.obtainMessage(WHAT_EXECUTE_EXECEPTION);
					msg.obj = e;
					msg.arg1 = mId;
					msg.setData(params);
					msg.sendToTarget();
				}
			}
		}

	}

	private static class InternalHandler extends Handler {

		private WeakReference<AsyncExecuteCallback> mAsyncExecuteCallbackRef;

		public InternalHandler(AsyncExecuteCallback asyncExecuteCallback) {
			this.mAsyncExecuteCallbackRef = new WeakReference<AsyncExecuteCallback>(asyncExecuteCallback);
		}

		public AsyncExecuteCallback getAsyncExecuteCallback() {
			return mAsyncExecuteCallbackRef.get();
		}

		public void handleMessage(Message msg) {
			if (msg.what == WHAT_EXECUTE_EXECEPTION) {
				int id = msg.arg1;
				Exception e = (Exception) msg.obj;
				Bundle bundle = msg.getData();
				AsyncExecuteCallback callback = getAsyncExecuteCallback();
				if (callback != null) {
					callback.onException(id, bundle, e);
				}
			} else {
				int id = msg.what;
				Bundle result = msg.getData();
				AsyncExecuteCallback callback = getAsyncExecuteCallback();
				if (callback != null) {
					callback.onPostExecute(id, result);
				}
			}
		};
	};

}
